/* ************************************************************************ */
/*                                                                          */
/*  Copyright (c)2005 Russell Weir | damonsbane _at_ gmail                  */
/*                                                                          */
/* This library is free software; you can redistribute it and/or            */
/* modify it under the terms of the GNU Lesser General Public               */
/* License as published by the Free Software Foundation; either             */
/* version 3.0 of the License, or (at your option) any later version.       */
/*                                                                          */
/* This library is distributed in the hope that it will be useful,          */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of           */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        */
/* Lesser General Public License or the LICENSE file for more details.      */
/*                                                                          */
/* ************************************************************************ */

package tzparser;
import altdate.TimeSpecification;
import altdate.DaySpecification;

/*
INFO from zic.8

A rule line has the form
	Rule  NAME  FROM  TO    TYPE  IN   ON       AT    SAVE  LETTER/S

For example:
	Rule  US    1967  1973  -     Apr  lastSun  2:00  1:00  D

NAME	Gives the (arbitrary) name of the set of rules this rule is part of.

FROM:   Gives the first year in which the rule applies.  Any
	integer year can be supplied; the Gregorian calendar
	is assumed.  The word minimum (or an abbreviation)
	means the minimum year representable as an integer.
	The word maximum (or an abbreviation) means the
	maximum year representable as an integer.  Rules can
	describe times that are not representable as time
	values, with the unrepresentable times ignored; this
	allows rules to be portable among hosts with
	differing time value types.

TO:	Gives the final year in which the rule applies.  In
	addition to minimum and maximum (as above), the word
	only (or an abbreviation) may be used to repeat the
	value of the FROM field.

TYPE:	Gives the type of year in which the rule applies.
	If TYPE is - then the rule applies in all years
	between FROM and TO inclusive.  If TYPE is something
	else, then zic executes the command
		yearistype year type
	to check the type of a year:  an exit status of zero
	is taken to mean that the year is of the given type;
	an exit status of one is taken to mean that the year
	is not of the given type.

IN:	Names the month in which the rule takes effect. Month names may be abbreviated.

ON:
	For ON field of rules (onDay variable). Formats are:
	[INT]	 a date
	lastSun  the last Sunday in the month
	lastMon  the last Monday in the month
	Sun>=8   first Sunday on or after the eighth
	Sun<=25  last Sunday on or before the 25t

AT:
	2        time in hours
	2:00     time in hours and minutes
	15:00    24-hour format time (for times after noon)
	1:28:14  time in hours, minutes, and seconds
	-        equivalent to 0

	Can be suffixed with:
	w = time is local "wall clock" time,
	s = time is local "standard" time,
	u,g or z = time is universal time
	absence of an indicator, wall clock time is assumed

SAVE:
	Gives the amount of time to be added to local
	standard time when the rule is in effect.  This
	field has the same format as the AT field (although,
	of course, the w and s suffixes are not used).

LETTER/S:
	Gives the "variable part" (for example, the "S" or
	"D" in "EST" or "EDT") of time zone abbreviations to
	be used when this rule is in effect.  If this field
	is -, the variable part is null.
*/

class ParseRule {
	/**
		Parse a timezone rule line
	*/
	public static function parse(line:String) : Rule {
		line = StringTools.trim(line);
		//Rule  2 NAME  3 FROM  4 TO    5TYPE   6 IN    7 ON    8 AT    9 SAVE  10 LETTER/S
		//Rule	Algeria	1916	only	-	Jun	14	23:00s	1:00	S
		var p = line.lastIndexOf("#");
		if(p>=0)
			line = StringTools.trim(line.substr(0,p));
		var r = ~/\s+/g;
		line = r.replace(line," ");
		//var r = ~/^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+([-|\S]+)$/;
		//if(!r.match(line))
		//	return null;
		var parts = line.split(" ");
		var last = parts.length - 1;
		if(last < 0)
			return null;

		if(parts[0] != "Rule")
			return null;

		var rule = new Rule();
		var until = new Until();
		rule.name = parts[1];

		// FROM:
		rule.validFromYear = getNum(parts[2]);

		// TO:
		until.year = getNum(parts[3], rule.validFromYear);

		// TYPE:
		if(parts[4] != "-")
			throw "Invalid RULE type " + parts[4];

		// IN:
		until.month = altdate.Tools.monthToInt(parts[5]);
		if(until.month == null)
			throw "Invalid month " + parts[5];

		// ON:
		// Null<{ dt: DayType, date: Null<Int>, dow: Null<Int> }>
		until.onDay = DaySpecification.parse(parts[6]);

		// AT:
		until.atTime = TimeSpecification.parse(parts[7]);

		// SAVE:
		rule.save = TimeSpecification.parse(parts[8]);

		// LETTER/S:
		if(parts[9] == "-")
			rule.letter = "";
		else
			rule.letter = parts[9];

		rule.until = until;

		return rule;
	}

	/**
		Parse Rule FROM and TO field
	*/
	public static function getNum(s : String, ?onlyVal : Null<Int>) {
		switch(s) {
		case "only": // present in "Rule: TO" field only
			if(onlyVal == null)
				throw Std.string(here.methodName + " only val missing");
			return onlyVal;
		case "maximum":
			return Rule.MAXYEAR;
		case "max":
			return Rule.MAXYEAR;
		case "minimum":
			return Rule.MINYEAR;
		case "min":
			return Rule.MINYEAR;
		}
		// should be all numbers now
		var r = ~/^[0-9]+$/;
		if(! r.match(s) )
			throw here.methodName + " Invalid number field [" + s + "]";
		return Std.parseInt(r.matched(0));
	}





}

